=begin
 *
 * File: sif03_definitions.rb
 * S-expression Interpreter Framework (SIF).
 * Version 1.0
 *
 * SIF03: Same as SIF02 interpreter, but with additional special forms that
 * allow imperative programming. New special forms: begin, set! and while. New
 * primitive procedure: print.
 *
 * Copyright (C) 2006-2008 by Ariel Ortiz <ariel.ortiz@itesm.mx>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
=end

require 'sif02_definitions'

#--------------------------------------------------------------------
class BeginNode < Node

  special_form 'begin'

  def initialize(args)
    @args = args.collect {|x| Node.build(x)}
  end

  def interpret(context)
    r = nil
    @args.each do |x|
      r = x.interpret(context)
    end
    return r
  end

end

#--------------------------------------------------------------------
class Environment

  def initialize(first_frame={})
    @frame_list = [first_frame]
  end

  def []=(k, value)
    @frame_list[0][k] = value
  end

  def [](k)
    @frame_list.each do |frame|
      return frame[k] if frame.has_key?(k)
    end
    return nil
  end

  def dup
    return Environment.new.init_frame_list(@frame_list)
  end

  def modify(k, value)
    @frame_list.each do |frame|
      if frame.has_key?(k)
        frame[k] = value
        return
      end
    end
  end

  def has_key?(k)
    @frame_list.each do |frame|
      if frame.has_key?(k)
        return true
      end
    end
    return false
  end

protected

  def init_frame_list(frame_list)
    @frame_list = frame_list.dup
    @frame_list[0, 0] = {}
    return self
  end

end

#--------------------------------------------------------------------
class SetNode < Node

  special_form 'set!'

  def initialize(args)
    if args.length == 2
      if args[0].kind_of?(Symbol)
        @varname = args[0]
        @value = Node.build(args[1])
      else
        raise InterpreterError.new("Invalid variable name in set!: '#{args[0]}'")
      end
    else
      raise InterpreterError.new('set! takes two arguments')
    end
  end

  def interpret(context)
    if !context.has_key?(@varname)
      raise InterpreterError.new("Cannot set an undefined variable: '#{@varname}'")
    else
      context.modify(@varname, @value.interpret(context))
      return nil
    end
  end

end

#--------------------------------------------------------------------
Primitives.include('print') do |args|
  args.each do |x|
    print x.to_sexp
  end
  puts
end

#--------------------------------------------------------------------
class WhileNode < Node

  special_form 'while'

  @@stack = []

  def initialize(args)
    raise InterpreterError.new('while takes two arguments') if args.length != 2
    @condition = Node.build(args[0])
    @body = Node.build(args[1])
  end

  def interpret(context)
    callcc do |cont|
      @@stack.push(cont)
      while @condition.interpret(context) != []
        @body.interpret(context)
      end
    end
    @@stack.pop
    return nil
  end

  def WhileNode.do_break
    @@stack.last.call if !@@stack.empty?
  end

end

#--------------------------------------------------------------------
Primitives.include('break') do |args, ctx|
  WhileNode.do_break
end

